#include "kernel/library.hpp"
#include "yocto/lua/lua-config.hpp"

Library:: ~Library() throw()
{
	
}

Library:: Library() :
species_(),
closed_(false)
{
}

void Library:: close() throw() { closed_ = true; }
bool Library:: is_closed() const throw() { return closed_; }

void Library:: reset_permeabilities() const throw()
{
	for( SpeciesSet::const_iterator i = species_.begin(); i != species_.end(); ++i )
	{
		const Species &species = *i;
		species.Pf = 1;
	}
}

bool Library:: has( const string &id ) const throw()
{
	return species_.search( id ) != NULL;
}

void Library:: append( const Species &s )
{
	// check status
	if( closed_ )
		throw exception("Library is closed: can't append '%s'", s.name.c_str() );
	
	// insert species
	if( ! species_.insert( s ) )
		throw exception("Library(Multiple '%s')", s.name.c_str() );
	
	// update its index
	Species   &last      = species_.back();
	(size_t &)(last.idx) = species_.size();
	
}

const Species & Library:: operator[]( const string & _name ) const
{
	const Species *sp = species_.search( _name );
	if( !sp ) throw exception("*** No Species[%s]", _name.c_str());
	return *sp;
}

size_t Library:: size() const throw() { return species_.size(); }


const Species & Library::operator()( size_t index ) const throw()
{
	assert(index>0);
	assert(index<=size());
	SpeciesSet::const_iterator i = species_.begin();
	return *(i+(--index));
}

void Library:: load( lua_State *L, const string &library_name )
{
	std::cerr << "---------------- Loading Library '" << library_name << "' ----------------" << std::endl;
	//--------------------------------------------------------------------------
	// set the zero permeability function
	//--------------------------------------------------------------------------
	lua_settop(L,0);
	lua_getglobal(L,BIO_PH_SP_ZERO);
	switch( lua_type(L,-1) )
	{
		case LUA_TNIL: {
			const string SP_Zero = vformat("function %s(zeta)\n\treturn 0\nend\n",BIO_PH_SP_ZERO);
			Lua::Config::DoString( L, SP_Zero ); }
			break;
			
		case LUA_TFUNCTION:
			break;
			
		default:
			throw exception("Invalid exisiting '%s'", BIO_PH_SP_ZERO);
	}
	
	//--------------------------------------------------------------------------
	// find the library of species
	//--------------------------------------------------------------------------
	lua_settop(L, 0);
	lua_getglobal( L, library_name.c_str() );
	if( ! lua_istable(L,-1) )
		throw exception("*** Library::load(no valid '%s')", library_name.c_str() );
	
	const size_t n = lua_objlen(L, -1);
	std::cerr << "-- Found " << n << " species" << std::endl;
	for( size_t i=1; i<=n; ++i )
	{
		//======================================================================
		//-- extract table=species info
		//======================================================================
		lua_rawgeti(L,-1,i);
		if( ! lua_istable(L,-1) )
			throw exception("*** Library: invalid species #%u", unsigned(i));
		
		//======================================================================
		//-- parse table = species
		//======================================================================
		
		//----------------------------------------------------------------------
		//-- name
		//----------------------------------------------------------------------
		lua_rawgeti(L, -1, 1);
		
		if( lua_isnil(L,-1) )
			break;
		
		if( !lua_isstring(L, -1) )
			throw exception("*** Library: invalid type for species name");
		
		size_t      name_len = 0;
		const char *name_txt = lua_tolstring(L, -1, &name_len);
		const string name( name_txt, name_len );
		std::cerr << "-- <" << name << ">";
		lua_pop(L,1); 
		
		//----------------------------------------------------------------------
		//-- charge
		//----------------------------------------------------------------------
		lua_rawgeti(L,-1,2);
		if( ! lua_isnumber(L, -1) )
			throw exception("*** Library: invalid type for species charge");
		const int Z = int( lua_tonumber(L, -1) );
		std::cerr << ",Z=" << Z;
		lua_pop(L,1);
		
		//----------------------------------------------------------------------
		//-- permeability
		//----------------------------------------------------------------------
		string sp_name;
		lua_rawgeti(L,-1,3);
		switch( lua_type(L,-1) )
		{
			case LUA_TNIL:
				std::cerr << ",permeability <" << BIO_PH_SP_ZERO << ">" ;
				sp_name = BIO_PH_SP_ZERO;
				break;
				
			case LUA_TSTRING:
			{
				size_t      sp_len = 0;
				const char *sp_txt = lua_tolstring(L, -1, &sp_len );
				sp_name.assign(sp_txt,sp_len);
			}
				std::cerr << ",permeability <" << sp_name << ">";
				break;
				
			default:
				throw exception("*** Library: invalid type for species permeability");
		}
		
		
		lua_pop(L,1);
		
		//----------------------------------------------------------------------
		// append species
		//----------------------------------------------------------------------
		const Species s( name, Z, L, sp_name );
		append( s );
		std::cerr << std::endl;
		
		//======================================================================
		//-- clean up stack
		//======================================================================
		lua_pop(L,1);
	}
	std::cerr << "-- Testing Permeabilities..." << std::endl;
	for( SpeciesSet::iterator i = species_.begin(); i != species_.end(); ++i )
	{
		Species &sp = *i;
		(void) sp.SP(0);
	}
	std::cerr << "---------------- Loaded  Library '" << library_name << "' ----------------" << std::endl << std::endl;
}
